/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/
// 声明为常量，方便代码提示，避免硬编码
const Pending = 'pending'; // 等待
const FulFilled = 'fulfilled'; // 成功
const Rejected = 'rejected'; // 失败

// Promise 可以通过new创建，是个类
class MyPromise{
    // 构造函数接收一个执行器函数作为参数
    constructor(executor){
        try{
            // 执行器立即执行（同步执行），并接收resolve，reject函数作为参数
            // 外部调用者通过调用resolve、reject改变promise状态
            executor(this.resolve,this.reject)
        }catch(e){
            // 捕获执行器函数异常，reject
            this.reject(e)
        }
    }

    // 状态 初始化为Pending
    status = Pending
    // 成功后的值
    value = undefined
    // 失败的原因
    reason = undefined
    // 成功回调函数队列
    fulfillHandlers = []
    // 失败回调函数队列
    rejectHandlers = []

    // 箭头函数，因为执行器内部直接调用 resolve(xxx),reject(xxx),将内部this与该Promise实例绑定
    resolve = value=>{
        // Promise状态只能由pending ->fulfilled 或 pending ->rejected, 一旦确定就不能再更改
        if(this.status !== Pending) return
        // 变更状态
        this.status = FulFilled
        // 记录成功后的值
        this.value = value

        // 从前往后，依次执行
        while(this.fulfillHandlers.length) this.fulfillHandlers.shift()(value)
    }
    // 箭头函数
    reject = reason =>{
        // 非pending态，则返回
        if(this.status !== Pending) return
        // 变更状态
        this.status = Rejected
        // 记录失败的原因
        this.reason = reason

        // 从前往后，依次执行
        while(this.rejectHandlers.length) this.rejectHandlers.shift()(reason)
    }

    then(fulfillHandler,rejectHandler){
        // 穿透现象，如果then参数为空，则往下传递，可继续在后面then接收原来返回值， 包括异常一直传递到第一个catch
        fulfillHandler = fulfillHandler ? fulfillHandler : v => v
        rejectHandler = rejectHandler ? rejectHandler : reason => { throw reason }

        // then函数返回一个promise
        let promise = new MyPromise((rs,rj)=>{
            if(this.status === FulFilled){
                // fulfill状态
                // 1、Promise.then是异步操作，这里用settimeout模拟
                // 2、改成异步操作，这里才能取到promise对象
                setTimeout(() => {
                    try{
                        let x = fulfillHandler(this.value)
                        this.resolvePromise(promise, x, rs, rj)
                    }catch(e){
                        rj(e)
                    }
                }, 0);
            }else if(this.status === Rejected){
                // rejected状态
                setTimeout(() => {
                    try{
                        let x = rejectHandler(this.reason)
                        this.resolvePromise(promise, x, rs, rj)
                    }catch(e){
                        rj(e)
                    }
                }, 0);
            }else{
                // pending状态
                this.fulfillHandlers.push(value => {
                    setTimeout(() => {
                        try{
                            let x = fulfillHandler(value)
                            this.resolvePromise(promise, x, rs, rj)
                        }catch(e){
                            rj(e)
                        }
                    }, 0);
                })
                this.rejectHandlers.push(reason => {
                    setTimeout(() => {
                        try{
                            let x = rejectHandler(reason)
                            this.resolvePromise(promise, x, rs, rj)
                        }catch (e){
                            rj(e)
                        }
                    }, 0);
                })
            }
        })

        return promise
    }

    resolvePromise(promise, x, rs ,rj){
        // 处理回调函数中返回then的返回值的问题，会导致循环引用
        if(promise === x) throw new TypeError('Chaining cycle detected for promise #<Promise>')
        
        if(x instanceof MyPromise){
            // 返回值为Promise
            x.then(rs,rj)
        }else{
            // 返回值为普通值
            rs(x)
        }
    }

    // catch直接调用then，可捕获catch所在位置之前的所有异常
    catch(rejectHandler){
        return this.then(undefined,rejectHandler);
    }

    static resolve(x){
        if(x instanceof MyPromise){
            // 参数是Promise,则返回这个Promise实例本身
            return x
        }else{
            // 参数是普通值
            return new MyPromise(rs=>{
                rs(x)
            })
        }
    }

    // 返回一个以x为失败原因的Promise
    static reject(x){
        return new MyPromise((rs,rj)=>{
            rj(x)
        })
    }

    // all方法接收一个数组，数组元素可以为普通值或Promise对象
    static all(array){
        // 成功后的值是一个数组，按异步调用的顺序存放，即和array一一对应
        let results = []
        let index = 0
     
        // 返回一个Promise对象
        return new Promise((rs,rj)=>{
            // 辅助函数用来计数，返回结果达到array.length即代表所有promise完成
            function addData(key,value){
                results[key] = value
                index ++
                if(index === array.length){
                    rs(results)
                }
            }

            for(let i=0; i<array.length; i++){
                let x = array[i]
                if(x instanceof MyPromise){
                    x.then(value=>{
                        addData(i, value)
                    },reason=>{
                        // 任何一个失败，则结束
                        rj(reason)
                    })
                }else{
                    addData(i, x)
                }
            }
        })
    }

    // 里面哪个结果获得的快，就返回那个结果，不管结果本身是成功状态还是失败状态。
    static race(array){
        return new Promise((rs,rj)=>{
            for(let i=0; i<array.length; i++){
                let x = array[i]
                if(x instanceof MyPromise){
                    x.then(rs,rj)
                }else{
                    rs(x)
                }
            }
        })
    }

    finally(callback){
        // 1.finally的返回是个Promise对象，需要把上一个then的结果透传下去
        // 2.finally里如果有异步操作，需要等待异步结束
        return this.then(value => {
            return MyPromise.resolve( callback() ).then( () => value )
        }, reason => {
            return MyPromise.resolve( callback() ).then( () => { throw reason } )
        })
    }
}
